/*
 * 
 * 
 *
 * 
 */

#include <common.h>
#include <bootretry.h>
#include <cli.h>
#include <command.h>
#include <console.h>
#include <dm.h>
#include <edid.h>
#include <environment.h>
#include <errno.h>
#include <i2c.h>
#include <malloc.h>
#include <asm/byteorder.h>
#include <linux/compiler.h>
#include <pwm.h>
/*↓*↓*↓*↓*↓*↓*↓**nova_lcd_logo**↓*↓*↓*↓*↓*↓*↓*/
#include <spi.h>
#include <asm/gpio.h>
#include <asm/unaligned.h>
#include <bmp_layout.h>
#include <mapmem.h>
#include <part.h>
#include <config.h>
#include <image.h>
#include <linux/ctype.h>
#include <ext4fs.h>
#include <linux/stat.h>
#include <fs.h>
#include <linux/math64.h>
/*↑*↑*↑*↑*↑*↑*↑**nova_lcd_logo**↑*↑*↑*↑*↑*↑*↑*/

extern int crc_flag;


/*↓*↓*↓*↓*↓*↓*↓*↓*↓*↓*↓*↓*↓*↓****nova_lcd_logo****↓*↓*↓*↓*↓*↓*↓*↓*↓*↓*↓*↓*↓*↓*/
/******************************************************************************
 *函数名：nova_lcd_backlight_enable；nova_front_panel_power_enable
 *参  数：void无
 *返回值：void无
 *作  用：点亮LCD背光
 ******************************************************************************/
void nova_front_panel_power_enable(void) {
	run_command("gpio set H3", 0);
}

void nova_lcd_backlight_enable(void) {
	run_command("gpio clean I2", 0);
}

/******************************************************************************
 *函数名：spi_gpio_init;nova_xxxcards_power_on;nova_fan_power_on;
 *参  数：void无
 *返回值：void无
 *作  用：初始化spi所用的各个gpio引脚，设为输出，并初始化值为0
 ******************************************************************************/
void nova_spi_gpio_init(void) {
	//spi_dc
	run_command("gpio clean C5", 0);
	//spi_rst
	run_command("gpio clean C15", 0);
	//spi_cs
	run_command("gpio clean I7", 0);
	//spi_clk
	run_command("gpio clean I6", 0);
	//spi_do
	run_command("gpio clean I8", 0);
}

/*for nova inputcard、outputcard、processcard gpio init */
void nova_xxxcards_power_on(void) {
	// processcard gpio is GPIO5_C3_d,the pin num in uboot is F19
	run_command("gpio set F19", 0);
	// inputcard gpio is GPIO6_A1_d,the pin num in uboot is G1
	run_command("gpio set G1", 0);
	// outputcard gpio is GPIO5_C2_d,the pin num in uboot is F18
	run_command("gpio set F18", 0);
}

void nova_xxxcards_power_off(void) {
	// processcard gpio is GPIO5_C3_d,the pin num in uboot is F19
	run_command("gpio clear F19", 0);
	// inputcard gpio is GPIO6_A1_d,the pin num in uboot is G1
	run_command("gpio clear G1", 0);
	// outputcard gpio is GPIO5_C2_d,the pin num in uboot is F18
	run_command("gpio clear F18", 0);
}

/*for nova fan gpio init */
void nova_fan_power_on(void) {
	// fan1 gpio is GPIO7_A6_u,the pin num in uboot is H6
	run_command("gpio set F18", 0);
	// fan2 gpio is GPIO7_A5_d,the pin num in uboot is H5
	run_command("gpio set G0", 0);
	// fan3 gpio is GPIO5_B3_u,the pin num in uboot is F11
	run_command("gpio set G2", 0);
	// fan3 gpio is GPIO5_B3_u,the pin num in uboot is F11
	run_command("gpio set G5", 0);
	// fan3 gpio is GPIO5_B3_u,the pin num in uboot is F11
	run_command("gpio set G8", 0);
}

void nova_fan_power_off(void) {
	// fan1 gpio is GPIO7_A6_u,the pin num in uboot is H6
	run_command("gpio clear F18", 0);
	// fan2 gpio is GPIO7_A5_d,the pin num in uboot is H5
	run_command("gpio clear G0", 0);
	// fan3 gpio is GPIO5_B3_u,the pin num in uboot is F11
	run_command("gpio clear G2", 0);
	// fan3 gpio is GPIO5_B3_u,the pin num in uboot is F11
	run_command("gpio clear G5", 0);
	// fan3 gpio is GPIO5_B3_u,the pin num in uboot is F11
	run_command("gpio clear G8", 0);
}

/******************************************************************************
 *函数名：do_spi_xfer_val
 *参  数：bus:spi2, cs:0, val:要传的值。 length：要传值的长度
 *返回值：void无
 *作  用：发送数据和命令大小的值
 ******************************************************************************/
static int do_spi_xfer_val(int bus, int cs, u8 val, int length) {
	struct spi_slave *slave;
	int ret = 0;

#ifdef CONFIG_DM_SPI
	char name[30], *str;
	struct udevice *dev;

	snprintf(name, sizeof(name), "generic_%d:%d", bus, cs);
	str = strdup(name);
	if (!str)
		return -ENOMEM;
	ret = spi_get_bus_and_cs(bus, cs, 40000000, 0, "spi_generic_drv",
				 str, &dev, &slave);
	if (ret)
		return ret;
#else
	slave = spi_setup_slave(bus, cs, 40000000, 0);
	if (!slave) {
		printf("Invalid device %d:%d\n", bus, cs);
		return -EINVAL;
	}
#endif

	ret = spi_claim_bus(slave);
	if (ret)
		goto done;
	
	ret = spi_xfer(slave, length*8, &val, NULL,
		       SPI_XFER_BEGIN | SPI_XFER_END);
#ifndef CONFIG_DM_SPI
	/* We don't get an error code in this case */
	if (ret)
		ret = -EIO;
#endif
	if (ret) {
		printf("Error %d during SPI transaction\n", ret);
	} else {
		/*nothing to do*/
	}
done:
	spi_release_bus(slave);
#ifndef CONFIG_DM_SPI
	spi_free_slave(slave);
#endif

	return ret;
}

/******************************************************************************
 *函数名：spi_send_data
 *参  数：要发送的命令或数据的内容
 *返回值：void无
 *作  用：发送命令或数据中的内容
 ******************************************************************************/
void spi_send_data(unsigned char val) {
	do_spi_xfer_val(2, 0, val, 1);
}

/******************************************************************************
 *函数名：spi_write_comd
 *参  数：要写入的命令值
 *返回值：void无
 *作  用：写命令
 ******************************************************************************/
void spi_write_comd(unsigned char val) {
	// DC引脚拉低，表示发送命令
	//spi_dc
	run_command("gpio clean C5", 0);
	// 发送接下来的8位数据
	spi_send_data(val);
}

/******************************************************************************
 *函数名：spi_write_data
 *参  数：要写入的数据值
 *返回值：void无
 *作  用：写数据
 ******************************************************************************/
void spi_write_data(unsigned char val) {
	// DC引脚置高，表示发送数据
	//spi_dc
	run_command("gpio set C5", 0);
	// 发送接下来的8位数据
	spi_send_data(val);
}

/******************************************************************************
 *函数名：lcd_set_pos
 *参  数：xs:x轴起始位置；xe:x轴结束位置；ys:y轴起始位置；ye:y轴结束位置；
 *返回值：void无
 *作  用：设定屏幕显示位置（？是否需要设置某个寄存器？）
 ******************************************************************************/
void lcd_set_pos(unsigned int xs, unsigned int xe,
											 unsigned int ys, unsigned int ye) {
	spi_write_comd(0x2A);
	spi_write_data(xs>>8);
	spi_write_data(xs&0xff);
	spi_write_data(xe>>8);
	spi_write_data(xe&0xff);
	
	spi_write_comd(0x2B);
	spi_write_data(ys>>8);
	spi_write_data(ys&0xff);
	spi_write_data(ye>>8);
	spi_write_data(ye&0xff);
	
	spi_write_comd(0x2C);
}

/******************************************************************************
 *函数名：lcd_draw_bmp_pic
 *参  数：pic:要显示的图片的地址
 *返回值：void无
 *作  用：显示图片
 ******************************************************************************/
#define ROW 		320
#define COL 		480
#define PIC_HEIGHT	480
#define PIC_WIDTH  	320
 
 void lcd_draw_bmp_pic(uchar *pic) {
	int i,j,k=0;
	lcd_set_pos(((COL-PIC_HEIGHT)/2), ((COL-PIC_HEIGHT)/2)+PIC_HEIGHT-1, 0, PIC_WIDTH-1);
	//spi_dc
	run_command("gpio set C5", 0);
	//printf("lcd_draw_bmp_pic display\n");
	for (i=0; i<PIC_WIDTH; i++) {
		for (j=0; j<PIC_HEIGHT; j++) {
			do_spi_xfer_val(2, 0, (pic[k+2]&0xfc), 1);
			do_spi_xfer_val(2, 0, (pic[k+1]&0xfc), 1);
			do_spi_xfer_val(2, 0, (pic[k+0]&0xfc), 1);
			k += 3;
		}
	}
}

/******************************************************************************
 *函数名：spi_lcd_ili9488_init
 *参  数：void无
 *返回值：void无
 *作  用：lcd驱动IC的初始化配置
 ******************************************************************************/
void spi_lcd_ili9488_init(void) {
	// 置高重启引脚，并延时
	//spi_rst
	run_command("gpio set C15", 0); mdelay(1);//可以选择msleep（1）；
	// 拉低重启引脚，并延时
	run_command("gpio clean C15", 0); mdelay(120);
	// 置高重启引脚，并延时
	run_command("gpio set C15", 0); mdelay(120);
	// 配置ili9488
		//Power Control 1
		spi_write_comd(0xC0);
		spi_write_data(0x0f);	// VREG1OUT = +5.0v
		spi_write_data(0x0c);	// VREG2OUT = -5.0v
		//Power Control 2
		spi_write_comd(0xC1);
		spi_write_data(0x41);	// VGH x 5;VGL x 4;(default)
		//VCOM Control(VCOM是液晶分子偏转的参考电压)
		spi_write_comd(0xC5);
		spi_write_data(0x00);	// nVM = 0:NV memory is not programmed
		spi_write_data(0x3A);	// VCM_REG [7:0] = 0x3A; generate VCOM voltage = -1.09375
		spi_write_data(0x80);	// VCM_REG_EN = 1:VCOM value from VCM_REG [7:0]
		//Memory Access Control(设置图像正反左右对角等)
		spi_write_comd(0x36);
		spi_write_data(0x28);	// MY=1, MX = 1, MV = 1,BGR = 1;
		//Interface Pixel Format
		spi_write_comd(0x3A);
		spi_write_data(0x56);	// 18 bits/pixel
		//Frame Rate Control
		spi_write_comd(0xB1);
		spi_write_data(0xA0);	// Division Ratio = Fosc;Frame rate = 60HZ
		//spi_write_data(0x11);
		//Display Inversion Control
		spi_write_comd(0xB4);
		spi_write_data(0x02);	// Inversion = 2 dot inversion;
		//Set Image Function
		spi_write_comd(0xE9);
		spi_write_data(0x00); 	// default
		//Interface Mode Control
		spi_write_comd(0xB0);
		spi_write_data(0x00); 	// default
		//Adjust Control 3
		spi_write_comd(0xF7);
		spi_write_data(0xA9); 	// default
		spi_write_data(0x51); 	// default
		spi_write_data(0x2C); 	// default
		spi_write_data(0x82); 	// default
		//Positive Gamma Control
		spi_write_comd(0xE0);
		spi_write_data(0x00);	// Set the gray scale voltage to adjust the gamma characteristics of the TFT panel
		spi_write_data(0x10);	// ...
		spi_write_data(0x14);
		spi_write_data(0x03);
		spi_write_data(0x0E);
		spi_write_data(0x04);
		spi_write_data(0x36);
		spi_write_data(0x56);
		spi_write_data(0x4B);
		spi_write_data(0x04);
		spi_write_data(0x0C);
		spi_write_data(0x0A);
		spi_write_data(0x30);
		spi_write_data(0x34);
		spi_write_data(0x0F);
		//Negative Gamma Control
		spi_write_comd(0xE1);	
		spi_write_data(0x00);	// Set the gray scale voltage to adjust the gamma characteristics of the TFT panel.
		spi_write_data(0x0E);	// ...
		spi_write_data(0x13);
		spi_write_data(0x03);
		spi_write_data(0x10);
		spi_write_data(0x06);
		spi_write_data(0x3E);
		spi_write_data(0x34);
		spi_write_data(0x55);
		spi_write_data(0x05);
		spi_write_data(0x0F);
		spi_write_data(0x0E);
		spi_write_data(0x3A);
		spi_write_data(0x3E);
		spi_write_data(0x0F);
		//Display Function Control
		spi_write_comd(0xB6);
		spi_write_data(0x00);	// 30 set rgb
		spi_write_data(0x22);	// GS,SS 02，42，62

		spi_write_comd(0x21);

		spi_write_comd(0x11);
		mdelay(120);
}

/******************************************************************************
 *函数名：lcd_partial_set
 *参  数：sr:起始行， er:终止行
 *返回值：void无
 *作  用：设置屏幕的部分模式
 ******************************************************************************/
void lcd_partial_set(unsigned int sr, unsigned int er) {
	spi_write_comd(0x30);
	spi_write_data(sr>>8);
	spi_write_data(sr&0xff);
	spi_write_data(er>>8);
	spi_write_data(er&0xff);
	spi_write_comd(0x12);
}

/******************************************************************************
 *函数名：lcd_get_bmp
 *参  数：ulong addr:内存中存放图片的地址
 *返回值：返回bmp图片的数据的地址
 *作  用：去掉bmp的头信息，过去图片数据
 ******************************************************************************/
uchar *lcd_get_bmp(ulong addr) {
	uchar *bmap;
	struct bmp_image *bmp = map_sysmem(addr, 0);
	addr = map_to_sysmem(bmp);
	bmp = (struct bmp_image *)map_sysmem(addr, 0);
	bmap = (uchar *)bmp + get_unaligned_le32(&bmp->header.data_offset);
	//printf("lcd_get_bmp is ok!\n");
	return bmap;
}

int logo_load_from_ext4(char *dev_name, char *dev_part, char *dst_addr, char *file_path) {
	unsigned long addr;
	const char *addr_str;
	const char *filename;
	loff_t bytes;
	loff_t pos;
	loff_t len_read;
	int ret;
	unsigned long time;
	char *ep;

	if (fs_set_blk_dev(dev_name, dev_part, FS_TYPE_EXT))
		return 1;

	if (5 >= 4) {
		addr = simple_strtoul(dst_addr, &ep, 16);
		if (ep == dst_addr || *ep != '\0')
			return CMD_RET_USAGE;
	} else {
		addr_str = getenv("loadaddr");
		if (addr_str != NULL)
			addr = simple_strtoul(addr_str, NULL, 16);
		else
			addr = CONFIG_SYS_LOAD_ADDR;
	}

	filename = file_path;
	
	bytes = 0;

	pos = 0;

	time = get_timer(0);
	ret = fs_read(filename, addr, pos, bytes, &len_read);
	time = get_timer(time);
	if (ret < 0)
		return 1;

	printf("%llu bytes read in %lu ms", len_read, time);
	if (time > 0) {
		puts(" (");
		print_size(div_u64(len_read, time) * 1000, "/s");
		puts(")");
	}
	puts("\n");

	setenv_hex("fileaddr", addr);
	setenv_hex("filesize", len_read);

	return 0;
}

void nova_logo_display_on(void) {
	spi_write_comd(0x29); //display on
	nova_lcd_backlight_enable();
}

/*###################################主函数###################################
 #函数名：nova_lcd_logo
 #参  数：void 
 #返回值：
 #作  用：
 #############################################################################*/
int nova_lcd_logo(void) {
	char *s = NULL;
	
	/* 1. 初始化spi所需gpio*/
	nova_spi_gpio_init();
	
	/* 2. ili9488初始化 */
	spi_lcd_ili9488_init();

	/* 3. 从文件系统中获取logo图像 */
	s = getenv("logocmd");// ~~/include/configs/firefly-rk3288.h
	if(s) {
	     if (run_command(s, 0)) {
		    printf("cmd=(%s) run error!!\n", s);
			logo_load_from_ext4("mmc", "0:7", "0x04000000", "/usr/uhd100/logo/logo.bmp");
			printf("logo load by logo_load_from_ext4!!\n");
			return -1;
		 }
	} else {
	    printf("logocmd is Null\n");
		return -2;
	}
	
	/* 4. 加载logo图像（暂不显示） */
	lcd_draw_bmp_pic(lcd_get_bmp(0x04000000));
	
	
	return 0;
}

/*↑*↑*↑*↑*↑*↑*↑*↑*↑*↑*↑*↑*↑*↑****nova_lcd_logo****↑*↑*↑*↑*↑*↑*↑*↑*↑*↑*↑*↑*↑*↑*/

#define  MBI5041_CLK(val)  \
    do { \
         if(!!(val)) { \
           run_command("gpio set C9",0); \
         } else { \
           run_command("gpio clear C9",0); \
         }\
    }while(0)
		
#define  MBI5041_LE(val) \
	do { \
         if(!!(val)) { \
           run_command("gpio set C12",0); \
         } else { \
           run_command("gpio clear C12",0); \
       } \
    }while(0)

#define  MBI5041_SDIN(val) \
	do { \
         if(!!(val)) { \
           run_command("gpio set C13",0); \
         } else { \
           run_command("gpio clear C13",0); \
         }\
    }while(0)
		
#define  MAX_BITS_FOR_PORT   (16)
#define  MAX_CHIP_NUMS       (5)

unsigned short led_chan[MAX_CHIP_NUMS][MAX_BITS_FOR_PORT] = {
	[0] = {	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	 /*port15~8*/
			0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000}, /*port7~0*/
	
	[1] = {	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,  /* chip 3 */
			0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
	
	[2] = {	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	 /* chip 2 */
			0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
	
	[3] = {	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,  /* chip 1 */
			0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
	
	[4] = {	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,  /* chip 0 */
			0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000} 
};

/**
 * @brief mbi5041使能写状态缓存器
 * @param None
 * @return None
 */
void mbi5041_enbale(void) {  
	uint8_t i;

	//发送写使能
	MBI5041_LE(0);	
	for(i=0; i<MAX_BITS_FOR_PORT; i++) { 
		MBI5041_CLK(1);
		ndelay(2);
		MBI5041_CLK(0); 
		if(i==1){
		   MBI5041_LE(1);
		}
		if(i==15) {
			MBI5041_LE(0);
		}
		ndelay(2);
	}
}

/* Config 16次数据锁存+1次整体数据锁存命令实现 */
#define  MBI5041_CTRL_REG  (0x82B0)

/**
 * @brief mbi5041写状态缓存器 
 * @param None
 * @return None
 * @note 状态缓存器各个位的含义见mbi5041数据手册
 *       此函数固定写入0x83F0
 */
void mbi5041_config(void) {  
	uint8_t i;
	uint16_t conf_reg = MBI5041_CTRL_REG;// 写入状态缓存器F=1
	
	for(i=0; i<MAX_BITS_FOR_PORT; i++)
	{	
		if(0x8000 & conf_reg){
			MBI5041_SDIN(1);
		}else{
			MBI5041_SDIN(0);
		}	
		ndelay(2);		
		MBI5041_CLK(1);
		ndelay(2);
		if(i == 5){
			MBI5041_LE(1);
		}else if(i==15){
			MBI5041_LE(0);
		}
		MBI5041_CLK(0);
		conf_reg <<= 1;
	}
}

static void mbi5041_ledshow_data_for_port(uint16_t val , int bank) {
	int i =0;
	
	for(i=0; i<MAX_BITS_FOR_PORT; i++) {				
		if(0x8000 & val){
			MBI5041_SDIN(1);
		} else {
			MBI5041_SDIN(0);
			MBI5041_SDIN(0);
		}	
		if(i == MAX_BITS_FOR_PORT-1 && bank == MAX_CHIP_NUMS-1){
			MBI5041_LE(1);
		}
		ndelay(2);
		
		MBI5041_CLK(1);;
		ndelay(2);
		if(i == MAX_BITS_FOR_PORT-1 && MAX_CHIP_NUMS-1 == 4){
			MBI5041_LE(0);
		}
		MBI5041_CLK(0);
		val <<= 1;
	}
	
}

static void mbi5041_ledshow_global(void) {
	int i =0;
	
	for(i=0; i<MAX_BITS_FOR_PORT; i++) {	
		if(i == MAX_BITS_FOR_PORT-2){
			MBI5041_LE(1);
		}
		ndelay(2);
		MBI5041_CLK(1);
		ndelay(2);
		MBI5041_CLK(0);
		if(i == MAX_BITS_FOR_PORT-1){
			MBI5041_LE(0);
		}	
	}
	
}

void mbi5041_ledshow(void){
	uint8_t i;
	uint16_t val = 0;
	uint16_t bank = 0;
	
	mbi5041_enbale();
	mbi5041_config();
	
	/* data latch 启用 */
	for(i = 0; i <MAX_BITS_FOR_PORT; i++){
		for(bank = 0; bank < MAX_CHIP_NUMS; bank++){
			val = led_chan[(MAX_CHIP_NUMS-1)-bank][(MAX_BITS_FOR_PORT-1)-i];
			mbi5041_ledshow_data_for_port(val,bank);
		}			
	}
	
	/* global latch 启用 */
	mbi5041_ledshow_global();
}

void led_gpio_init(void) {
	run_command("gpio clear C9",0); /* PWM_CLK: HOST_D7/TS_VALID/CIF_HREF/GPIO2_B1_d */
	run_command("gpio clear C12",0);/* PWM_LATCH: CIF_D0/GPIO2_B4_d */
	run_command("gpio clear C13",0);/* PWM_SDIN: CIF_D1/GPIO2_B5_d */
}

int led_pwm_init(void) {
	struct udevice *dev;
	
	uclass_get_device(UCLASS_PWM, 0, &dev);
	
	pwm_set_config(dev, 0, 1000, 400);
	
	pwm_set_enable(dev, 0, 1);
	
	return 0;
}

int led_on(int chip,int port,unsigned short value) {
	led_chan[chip][port] = value;
	//printf("led chip%d port%d is value = %4x\n",chip , port, led_chan[chip][port]);
	mbi5041_ledshow();
	return 0;
}

enum i2c_err_op {
	I2C_ERR_READ,
	I2C_ERR_WRITE,
};

static int i2c_report_err(int ret, enum i2c_err_op op) {
	printf("Error %s the chip: %d\n",
	       op == I2C_ERR_READ ? "reading" : "writing", ret);

	return CMD_RET_FAILURE;
}

#ifdef CONFIG_DM_I2C
static struct udevice *i2c_cur_bus;
static int cmd_i2c_set_bus_num(unsigned int busnum) {
	struct udevice *bus;
	int ret;

	ret = uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus);
	if (ret) {
		debug("%s: No bus %d\n", __func__, busnum);
		return ret;
	}
	i2c_cur_bus = bus;

	return 0;
}
#define CONFIG_I2C_SET_DEFAULT_BUS_NUM
#define CONFIG_I2C_DEFAULT_BUS_NUMBER 2
static int i2c_get_cur_bus(struct udevice **busp) {
#ifdef CONFIG_I2C_SET_DEFAULT_BUS_NUM
	if (!i2c_cur_bus) {
		if (cmd_i2c_set_bus_num(CONFIG_I2C_DEFAULT_BUS_NUMBER)) {
			printf("Default I2C bus %d not found\n",
			       CONFIG_I2C_DEFAULT_BUS_NUMBER);
			return -ENODEV;
		}
	}
#endif

	if (!i2c_cur_bus) {
		puts("No I2C bus selected\n");
		return -ENODEV;
	}
	*busp = i2c_cur_bus;

	return 0;
}

static int i2c_get_cur_bus_chip(uint chip_addr, struct udevice **devp) {
	struct udevice *bus;
	int ret;

	ret = i2c_get_cur_bus(&bus);
	if (ret)
		return ret;

	return i2c_get_chip(bus, chip_addr, 1, devp);
}
#endif

/* 读取按键寄存器值 */
uint i2c_md(unsigned long chip_addr, unsigned long reg_addr) {
	uint chip = chip_addr;
	uint addr = reg_addr;
	uint length = 1;
	int alen  = -1;
	int ret;
	int nbytes;
	int linebytes;
	int j;
	unsigned char	linebuf[16];
	uint value;
#ifdef CONFIG_DM_I2C
	struct udevice *dev;
#endif
	
#ifdef CONFIG_DM_I2C
	ret = i2c_get_cur_bus_chip(chip, &dev);
	//printf("i2c_get_cur_bus_chip is ret is %d\n",ret);
	if (!ret && alen != -1) {
		ret = i2c_set_chip_offset_len(dev, alen);
	}
	if (ret) {
		return i2c_report_err(ret, I2C_ERR_READ);
	}
#endif

	nbytes = length;
	do {
		unsigned char *cp;

		linebytes = (nbytes > 16) ? 16 : nbytes;
#ifdef CONFIG_DM_I2C
		ret = dm_i2c_read(dev, addr, linebuf, linebytes);
#else
		ret = i2c_read(chip, addr, alen, linebuf, linebytes);
#endif
		//printf("(dm_)i2c_read is ret is %d\n",ret);
		if (ret)
			return i2c_report_err(ret, I2C_ERR_READ);
		else {
			// printf("%04x:", addr);
			cp = linebuf;
			for (j=0; j<linebytes; j++) {
				// printf(" %02x", *cp++);
				addr++;
			}
			// puts ("    ");
			cp = linebuf;
			for (j=0; j<linebytes; j++) {
				if ((*cp < 0x20) || (*cp > 0x7e)) {
					//puts (".");
				}else{
					//printf("%c", *cp);
				}
				cp++;
			}
			//putc ('\n');
		}
		nbytes -= linebytes;
	} while (nbytes > 0);
	value = linebuf[0];
	return value;
} 

void keypad_led_display(void) {
	int i,j;
	// 1. 红灯亮
	for(i=0;i<5;i++) {
		for(j=0;j<16;j+=2) {
			led_chan[i][j] = 0xffff;
		}
	}
	mbi5041_ledshow();
	mdelay(300);
	// 2. 黄灯亮
	for(i=0;i<5;i++) {
		for(j=1;j<16;j+=2) {
			led_chan[i][j] = 0x2222;
			if(i==0&&j==1) {
				led_chan[0][1] = 0x0000;
			}
		}
	}
	mbi5041_ledshow();
	mdelay(300);
	// 3. 绿灯亮
	for(i=0;i<5;i++) {
		for(j=0;j<16;j+=2) {
			led_chan[i][j] = 0x0000;
			if(i==0&&j==0) {
				led_chan[0][0] = 0xffff;
			}
		}
	}
	mbi5041_ledshow();
	mdelay(300);
	// 4. 关灯
	for(i=0;i<5;i++) {
		for(j=1;j<16;j+=2) {
			led_chan[i][j] = 0x0000;
		}
	}
	mbi5041_ledshow();
}

#define POWERKEY_VALUE 	0x01
#define POWERKEY_CHIP  	0x20
#define POWERKEY_REG	0
int do_nova_driver(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
	uint value;
	char *s = NULL;
	int env_variable_value;
	int reboot_enable;
	unsigned int gpio;
	int ret;
	char *str_gpio = "C6";

#if defined (FEATURE_SUPPORT_PROUHD_FOR_LINUX)
	/* 1. 打开前面版总电源 */
	nova_front_panel_power_enable();
	
	/* 2. 前面板风扇关闭 */
	nova_fan_power_off();
	
	/* 3. 各种卡掉电 */
	nova_xxxcards_power_off();
	
	/* 4. 加载logo图像（暂不显示） */
	nova_lcd_logo();
#endif

	s = getenv("nova_reboot");
	reboot_enable = (int)simple_strtol(s, NULL, 10);
	if(reboot_enable != 1) {
#if defined (FEATURE_SUPPORT_PROUHD_FOR_LINUX)
		/* 5. 检测powerkey是否按下 */
		s = getenv("powerkey");
		env_variable_value = (int)simple_strtol(s, NULL, 10);
		if( (env_variable_value==1) || (env_variable_value==2) ) {
			// 5.1 设置当前I2C总线为keypad所在的bus2
			printf("I2C:   ");
			run_command("i2c dev 2", 0);
			
			// 5.2 初始化按键灯
			led_pwm_init();
			led_gpio_init();
			
			// 5.3 循环检测powerkey是否按下
			printf("waitting for powerkey ...\n");
			do {
				value = ((i2c_md(POWERKEY_CHIP,POWERKEY_REG))^0xFF)&0x01;
				//printf("value is %02x\n",value);
			}while(value != POWERKEY_VALUE);
			
			// 5.4 电源灯亮（红）
			led_on(0,0,0xffff);
			
			// 5.5 前面板按键灯整体闪烁：红->黄->绿
			s = getenv("powerkey");
			env_variable_value = (int)simple_strtol(s, NULL, 10);
			if(env_variable_value == 2) {
				keypad_led_display();
			}
			//printf("powerkey is defined!\n");
		}
#elif defined (FEATURE_SUPPORT_F8_FOR_LINUX)
		str_gpio = "B25";
		
		ret = gpio_lookup_name(str_gpio, NULL, NULL, &gpio);
		if (ret) {
			printf("GPIO: '%s' not found\n", str_gpio);
		}
		ret = gpio_request(gpio, "pwr_gpio");
		if (ret && ret != -EBUSY) {
			printf("gpio: requesting pin %u failed\n", gpio);
		}
		gpio_direction_input(gpio);

		printf("waitting for powerkey ...\n");
		do {
			mdelay(100);
		}while(gpio_get_value(gpio));
#endif
	}else{
		printf("System reboot ...\n");
	}

#if defined (FEATURE_SUPPORT_PROUHD_FOR_LINUX)
	/* 6. 显示logo */
	nova_logo_display_on();
	
	/* 7. 处理卡、输入卡和输出卡上电 */
	nova_xxxcards_power_on();

	/* 8. 启动风扇，默认最高速，直到内核风扇驱动接管风扇 */
	//nova_fan_power_on();
#elif defined (FEATURE_SUPPORT_F8_FOR_LINUX)
	str_gpio = "B25";
		
	ret = gpio_lookup_name(str_gpio, NULL, NULL, &gpio);
	if (ret) {
		printf("GPIO: '%s' not found\n", str_gpio);
	}
	ret = gpio_request(gpio, "pwr_gpio");
	if (ret && ret != -EBUSY) {
		printf("gpio: requesting pin %u failed\n", gpio);
	}
	gpio_direction_input(gpio);
#endif

	/* 9. 保存当前环境变量 */
	/* 2018-1-30 15:06:11在这里每次启动都会执行一次saveenv，所以不合适，现已放到/common/env_common.c文件
	下，位置在第112行，set_default_env函数中 ，只有当CRC校验有问题时，才保存*/
	/* 2018-2-2 14:07:20但是测试发现env_common执行时，环境变量还不全，此时保存会导致环境变量丢失，所以现在修改为，
	在env_common.c中若检测到CRC校验问题时，设定一个crc_flag的全局变量，当检测到此环境变量时
	则执行saveenv */
	if (crc_flag == 1) {
		run_command("saveenv",0);
		crc_flag = 0;
		printf("saveenv is DONE!\n");
	}
	//run_command("saveenv",0);	//保存当前环境变量的原因是：BUG!!!:内核中的fw_setenv存在bug，当未保存uboot环境变量时，执行fw_setenv会造成环境变量被清空，导致系统启动失败！
	
	nova_fan_power_off();
    return 0;
}

/*------------------------------------------------------------------------------
 *命令整体说明：
 *	一、该命令默认在代码中直接运行。
 *		运行位置：uboot/cmd/pxe.c
 *		运行时机：在uboot启动读秒之后（为维护人员提供后门，以便开关该命令）
 *		运行现象：读秒后会在启动kernel前热等待，直到检测到电源键动作。
 *				  执行完命令功能后，继续启动kernel。做到伪开机状态。
 * 	二、执行nova_driver命令时，进入主函数后
 *		a. 首先给前面板上电
 *		b. 预加载logo图像，但是暂不显示
 * 		c. 紧接着检测powerkey环境变量值。
 * 			1. 当powerkey=1时：
 *			   	循环检测电源键是否动作，当检测到动作时，电源键亮红灯
 *			2. 当powerkey=2时：
 *			   	循环检测电源键是否动作，当检测到动作时，电源键亮红灯且键板整体闪灯
 * 			3. 当没有检测到powerkey环境变量或powerkey值不为1或2时：
 *				不检测电源键动作。
 *		d. 无论是否有环境变量，都会执行logo显示动作。
 *		e. 无论是否有环境变量，都会执行各种卡上电动作。
 *		f. 无论是否有环境变量，都会启动风扇（默认最高速）。
 *----------------------------------------------------------------------------*/
 
U_BOOT_CMD(nova_driver, 2, 0, do_nova_driver,
	"Enable powerkey , display logo on lcd and power on xxxxcards\n",
	"<powerkey|logo|cardpower|fan> <nova>\n");
